# IO.PY - Input and output classes and routines.
# HIT3046 AI for GAMES
# ------- Assignment 2
# Authors: Mike Blackney, 0906123
#          Alex Bruce, 5409438

import os, sys # Used for file system

import pygame # Pygame!  Pretty interface!
from pygame.locals import *  # Useful constants and classes

import pos

# Give some warnings if we don't have fonts or sound:
if not pygame.font: print 'Fonts have been disabled.'
if not pygame.font: print 'Sounds have been disabled.'

class SPRITE(pygame.sprite.Sprite):
    "Base class for our bitmap sprites."

    ImageDict = dict( ) # keeps track of the images already loaded

    def __init__( self, *args ):
        # Chain up first:
        pygame.sprite.Sprite.__init__( self )
        
        if len( args ) is not 2:
            print "Incorrect parameters passed during sprite creation!"
            raise SystemExit
        self.image, self.rect = self.LoadImage( args[0], args[1] )


    def LoadImage( self, name, transparency=None ):
        # If we've loaded it already, just return:
        if name in self.ImageDict:
            return self.ImageDict[name], self.ImageDict[name].get_rect( )
        
        # Loads an image from a file.
        fullname = os.path.join('images', name)

        try:
            image = pygame.image.load(fullname)
        except pygame.error, message:
            print 'Cannot load image:', name
            raise SystemExit, message
        
        image = image.convert( )
        
        if transparency is not None:
            if transparency is -1:
                transparency = image.get_at((0,0))
            image.set_colorkey(transparency, RLEACCEL)

        self.ImageDict[name] = image # cache the image for quick loading later
            
        return image, image.get_rect( ) # returns the image and its border

    def Position( self, location ):
        # Move the sprite to the new location:
        self.rect.center = location

class BUTTONSPRITE (object):
    "A sprite that has two states, up and down, and can be clicked."
    myGroup = None

    def __init__( self, *args ):
        if len(args) is not 3: # Expect: two filenames and transparency color
            print "Incorrect parameters passed during sprite creation!"
            raise SystemExit

        self.up_spr   = SPRITE( args[0], args[2] )
        self.down_spr = SPRITE( args[1], args[2] )
        self.isUp = True
        self.image = self.up_spr.image
        self.rect = self.up_spr.rect

    def TestPress( self ):
        if self.isUp:
            if self.rect.collidepoint( pygame.mouse.get_pos( ) ):
                self.isUp = False
                self.image = self.down_spr.image
                self.rect = self.down_spr.rect
                return True
        return False

    def TestRelease( self ):
        if not self.isUp:
            # If you release the mouse, all pressed buttons are released.
            self.isUp = True
            self.image = self.up_spr.image
            self.rect = self.up_spr.rect
            if self.rect.collidepoint( pygame.mouse.get_pos( ) ):
                return True
        return False

    def Position( self, location ):
        # Move both sprites to the new location:
        self.up_spr.rect.center = pos.Pos(location).to_list( )
        self.down_spr.rect.center = pos.Pos(location).to_list( )

    def add_internal( self, group ):
        self.myGroup = group
    def remove_internal( self, group ):
        if (self.myGroup is group):
            self.myGroup = None

class GAMESPRITE (BUTTONSPRITE):
    "A sprite representing a controllable game agent."
    def __init__( self, *args ):
        if len(args) is not 4: # Expect: parent agent, two filenames and transparency color
            print "Incorrect parameters passed during sprite creation!"
            raise SystemExit
        # Chain up:
        BUTTONSPRITE.__init__( self, args[1], args[2], args[3] )
        # Set our parent:
        self.parent = args[0]
    

class FEEDBACK (object):
    "This  singleton object is used for collecting a list of sprites and displaying them."

    class __impl: # Implementation using Singleton design pattern.
        # A list for all the game sprites and one for the bg elements:
        spriteList = pygame.sprite.Group( )
        backgroundList = pygame.sprite.Group( )

        def Init( self, BackgroundColor, ScreenSize=(1024,768), Flags=pygame.FULLSCREEN, Depth=32 ): # Inits pygame and display a surface.
            # Initialise the display:
            pygame.init( )
            self.screen = pygame.display.set_mode(ScreenSize, Flags, Depth)
            pygame.display.set_caption('HIT3046 AI for GAMES -- Assignment 2 -- Alex and Mike')
            # Create the surface:
            self.background = pygame.Surface(self.screen.get_size( ))
            self.background = self.background.convert( )
            self.background.fill(BackgroundColor)
            self.bgcolor = BackgroundColor
            # Display the surface:
            self.screen.blit(self.background, (0, 0))
            pygame.display.flip( )

            # Initialise the audio:
            self.snd_alarm = self.LoadSound('alarm.wav')
            self.snd_crisisaverted = self.LoadSound("crisisaverted.wav")
            self.snd_pistol = self.LoadSound("pistol.wav")
            self.snd_repair = self.LoadSound("repair.wav")
            self.snd_silencer = self.LoadSound("silencer.wav")
            self.snd_zzzz = self.LoadSound("zzzz.wav")
            self.snd_bomb = self.LoadSound("bomb.wav")
            self.snd_tick = self.LoadSound("tick.wav")
            self.snd_explosion = self.LoadSound("explosion.wav")
            self.snd_win = self.LoadSound("win.wav")

        def LoadSound(self, name):
            # Load an audio file and return a handle to it.
            class NoneSound:
                def play(self): pass
        
            if not pygame.mixer:
                return NoneSound()
    
            fullname = os.path.join('audio', name)
    
            try:
                sound = pygame.mixer.Sound(fullname)
            except pygame.error, message:
                print 'Cannot load sound:', fullname
                raise SystemExit, message
            return sound

        def FillBG( self, BackgroundColor ):
            # Fill the background with a given colour.
            self.bgcolor = BackgroundColor
            self.background.fill(BackgroundColor)
            
        def Render( self ):
            # RENDER SPRITES:
            self.screen.blit( self.background, (0,0) )
            self.backgroundList.draw( self.screen )
            self.spriteList.draw( self.screen )
            
        def Update( self ):
            # Flip the virtual screen to the screen:
            pygame.display.flip( )
            self.background.fill(self.bgcolor)
        
    # storage for the instance reference
    __instance = None

    def __init__(self): # Create singleton instance
        # Check whether we already have an instance
        if FEEDBACK.__instance is None:
            # Create and remember instance
            FEEDBACK.__instance = FEEDBACK.__impl( )

        # Store instance reference as the only member in the handle
        self.__dict__['_FEEDBACK__instance'] = FEEDBACK.__instance
        
    def __getattr__(self, attr): # Delegate access to implementation
        return getattr(self.__instance, attr)

    def __setattr__(self, attr, value): # Delegate access to implementation
        return setattr(self.__instance, attr, value)
